﻿/* **********************************************************************************
 *
 * Copyright (c) TMF.AscendedThemeKit.Unthemed.NET Project. All rights reserved.
 *
 * This source code is subject to terms and conditions of the Shared Source License
 * for TMF.AscendedThemeKit.Unthemed. A copy of the license can be found in the License.html file
 * at the root of this distribution. If you can not locate the Shared Source License
 * for TMF.AscendedThemeKit.Unthemed, please send an email to ascendlic@<TBD>.
 * By using this source code in any fashion, you are agreeing to be bound by
 * the terms of the Shared Source License for TMF.AscendedThemeKit.Unthemed.NET.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * **********************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace TMF.AscendedThemeKit.Unthemed.Windows.Forms
{
    /// <summary>
    /// Represents a set of 3 controls. They're working together to make a container with 2 resizables panels.
    /// </summary>
    //[Designer("TMF.AscendedThemeKit.Unthemed.Windows.Forms.Design.GradientSplitContainerDesigner, TMF.AscendedThemeKit.Unthemed.Design.v1.5, Culture=neutral, PublicKeyToken=5123e2ac4258b06a")]
    [Docking(DockingBehavior.AutoDock)]
    public class GradientSplitContainer : ContainerControl
    {
        #region Events
        /// <summary>
        /// Occurs when splitter was moved
        /// </summary>
        public event SplitterEventHandler SplitterMoved;
        /// <summary>
        /// Occurs when splitter is moving
        /// </summary>
        public event SplitterCancelEventHandler SplitterMoving;
        #endregion

        /// <summary>
        /// Builds a new SplitGradientContainer control.
        /// </summary>
        public GradientSplitContainer() {
            InitializeComponent();
            
            ChangeOrientation();
            LayoutControls();

            this.splitter.MouseDown += new MouseEventHandler(splitter_MouseDown);
            this.splitter.MouseUp += new MouseEventHandler(splitter_MouseUp);
            this.splitter.MouseMove += new MouseEventHandler(splitter_MouseMove);
            this.KeyUp += new KeyEventHandler(SplitGradientContainer_KeyUp);
        }

        /// <summary>
        /// Initialize components
        /// </summary>
        private void InitializeComponent() {
            this.splitter = new TMF.AscendedThemeKit.Unthemed.Windows.Forms.GradientSplitBar();
            this.panel1 = new TMF.AscendedThemeKit.Unthemed.Windows.Forms.GradientPanel();
            this.panel2 = new TMF.AscendedThemeKit.Unthemed.Windows.Forms.GradientPanel();
            this.SuspendLayout();
            // 
            // splitter
            // 
            this.splitter.GradientHighColor = System.Drawing.SystemColors.GradientActiveCaption;
            this.splitter.GradientLowColor = System.Drawing.SystemColors.ActiveCaption;
            this.splitter.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
            this.splitter.Location = new System.Drawing.Point(0, 0);
            this.splitter.Name = "splitter";
            this.splitter.Size = new System.Drawing.Size(151, 6);
            this.splitter.TabIndex = 0;
            this.splitter.Dock = DockStyle.Left;
            // 
            // panel1
            // 
            this.panel1.Location = new System.Drawing.Point(0, 0);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(151, 105);
            this.panel1.TabIndex = 0;
            this.panel1.Dock = DockStyle.Left;
            // 
            // panel2
            // 
            this.panel2.Location = new System.Drawing.Point(0, 0);
            this.panel2.Name = "panel2";
            this.panel2.Size = new System.Drawing.Size(151, 105);
            this.panel2.TabIndex = 0;
            this.panel2.Dock = DockStyle.Fill;
            //
            // SplitGradientContainer
            //
            this.Controls.Add(this.panel2);
            this.Controls.Add(this.splitter);
            this.Controls.Add(this.panel1);
            this.ResumeLayout(false);
        }

        #region private Methods
        /// <summary>
        /// organize components
        /// </summary>
        private void LayoutControls() {
            if (this.panel2Collapsed || this.panel1Collapsed)
                return;

            this.SuspendLayout();
            switch(this.orientation) {
                case Orientation.Vertical :
                    this.panel1.Width = this.splitterDistance;
                    this.splitter.Width = this.splitterWitdh;
                    break;
                case Orientation.Horizontal:
                    this.panel1.Height = this.splitterDistance;
                    this.splitter.Height = this.splitterWitdh;
                    break;
            }
            this.ResumeLayout();
        }

        /// <summary>
        /// Change controls order 
        /// </summary>
        private void ChangeOrientation() {
            if (this.panel1Collapsed || this.panel2Collapsed)
                return;

            this.SuspendLayout();
            switch (this.orientation) { 
                case Orientation.Vertical:
                    this.panel1.Dock = DockStyle.Left;
                    this.splitter.Dock = DockStyle.Left;
                    this.panel2.Dock = DockStyle.Fill;
                    this.splitter.Direction = GradientSplitBarDirection.Vertical;
                    this.splitter.Cursor = Cursors.VSplit;
                    break;
                case Orientation.Horizontal:
                    this.panel1.Dock = DockStyle.Top;
                    this.splitter.Dock = DockStyle.Top;
                    this.panel2.Dock = DockStyle.Fill;
                    this.splitter.Direction = GradientSplitBarDirection.Horizontal;
                    this.splitter.Cursor = Cursors.HSplit;
                    break;
            }

            if (this.isFixedSplitter)
                this.splitter.Cursor = Cursors.Default;
            this.ResumeLayout();
        }

        /// <summary>
        /// Collapses and opens panels
        /// </summary>
        private void CollapsePanel()
        {
            if (!(this.panel2Collapsed || this.panel1Collapsed)) {
                this.splitter.Visible = true;
                this.panel1.Visible = true;
                this.panel2.Visible = true;

                ChangeOrientation();
                LayoutControls();
                return;
            }

            this.SuspendLayout();
            this.splitter.Visible = false;
            if (this.panel1Collapsed) {
                this.panel1.Visible = false;
                this.panel2.Dock = DockStyle.Fill;
            }

            if (this.panel2Collapsed) {
                this.panel2.Visible = false;
                this.panel1.Dock = DockStyle.Fill;
            }
            this.ResumeLayout();
        }
        #endregion

        #region private members
        private FixedPanel fixedPanel = FixedPanel.None;
        private bool isFixedSplitter = false;
        private Orientation orientation = Orientation.Vertical;
        private GradientPanel panel1 = new GradientPanel();
        private GradientPanel panel2 = new GradientPanel();
        private GradientSplitBar splitter = new GradientSplitBar();
        private bool panel1Collapsed = false;
        private bool panel2Collapsed = false;
        private int panel1MinSize = 25;
        private int panel2MinSize = 25;
        private int splitterDistance = 50;
        private int splitterWitdh = 6;
        private bool splitterMoving = false;
        private int tempSplitterDistance = 50;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets if panel1 or panel2 is fixed on resize or not.
        /// </summary>
        [DefaultValue(FixedPanel.None)]
        [Category("Layout")]
        public FixedPanel FixedPanel {
            get { return this.fixedPanel; }
            set { this.fixedPanel = value; }
        }

        /// <summary>
        /// Gets or sets if the splitter is fixed
        /// </summary>
        [DefaultValue(false)]
        [Category("Layout")]
        public bool IsFixedSplitter
        {
            get { return this.isFixedSplitter; }
            set { 
                this.isFixedSplitter = value;
                if (value)
                {
                    this.splitter.Cursor = Cursors.Default;
                }
                else {
                    this.splitter.Cursor = (this.orientation == Orientation.Vertical ? Cursors.VSplit : Cursors.HSplit);
                }

            }
        }

        /// <summary>
        /// Gets or sets the orientation of splitter
        /// </summary>
        [DefaultValue(Orientation.Vertical)]
        [Category("Behavior")]
        public Orientation Orientation
        {
            get { return this.orientation; }
            set {
                if (this.orientation != value)
                {
                    this.orientation = value;
                    ChangeOrientation();
                    LayoutControls();
                }

            }
        }

        /// <summary>
        /// Get the panel on the top or on the left 
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Appearance")]
        public GradientPanel Panel1
        {
            get { return this.panel1; }
        }

        /// <summary>
        /// Get the panel on the bottom or on the right
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Appearance")]
        public GradientPanel Panel2
        {
            get { return this.panel2; }
        }

        /// <summary>
        /// Gets or sets if the panel1 is collapsed
        /// </summary>
        [DefaultValue(false)]
        [Category("Layout")]
        public bool Panel1Collapsed
        {
            get { return this.panel1Collapsed; }
            set {
                if (this.panel2Collapsed && value) {
                    throw new Exception("Panel1 and Panel2 can't be collapsed at same time !");
                }
                if (value != this.panel1Collapsed)
                {
                    this.panel1Collapsed = value;
                    CollapsePanel();
                }
            }
        }

        /// <summary>
        /// Gets or sets if the panel2 is collapsed
        /// </summary>
        [DefaultValue(false)]
        [Category("Layout")]
        public bool Panel2Collapsed
        {
            get { return this.panel2Collapsed; }
            set {
                if (this.panel1Collapsed && value)
                {
                    throw new Exception("Panel1 and Panel2 can't be collapsed at same time !");
                }
                if (value != this.panel2Collapsed)
                {
                    this.panel2Collapsed = value;
                    CollapsePanel();
                }
            }
        }

        /// <summary>
        /// Gets or sets the minimum size of the panel1
        /// </summary>
        [DefaultValue(25)]
        [Category("Layout")]
        public int Panel1MinSize
        {
            get { return this.panel1MinSize; }
            set { this.panel1MinSize = value; }
        }

        /// <summary>
        /// Gets or sets the minimum size of the panel2
        /// </summary>
        [DefaultValue(25)]
        [Category("Layout")]
        public int Panel2MinSize
        {
            get { return this.panel2MinSize; }
            set { this.panel2MinSize = value; }
        }

        /// <summary>
        /// Gets or sets the pixel distance between splitter and the top or the left edge
        /// </summary>
        [DefaultValue(50)]
        [Category("Layout")]
        public int SplitterDistance
        {
            get { return this.splitterDistance; }
            set {
                if (this.splitterDistance != value)
                {
                    this.splitterDistance = value;
                    LayoutControls();
                }
            }
        }

        /// <summary>
        /// Gets or sets the thickness of the splitter
        /// </summary>
        [DefaultValue(6)]
        [Category("Layout")]
        public int SplitterWidth
        {
            get { return this.splitterWitdh; }
            set {
                if (this.splitterWitdh != value)
                {
                    this.splitterWitdh = value;
                    LayoutControls();
                }
            }
        }
        #endregion

        #region Virtual methodes

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnSplitterMoving(object sender, SplitterCancelEventArgs e) {
            if (!e.Cancel && this.SplitterMoving != null) {
                this.SplitterMoving(sender, e);
            }

            switch (this.orientation)
            {
                case Orientation.Vertical:
                    this.tempSplitterDistance = this.SplitterDistance + e.MouseCursorX;
                    break;
                case Orientation.Horizontal:
                    this.tempSplitterDistance = this.SplitterDistance + e.MouseCursorY;
                    break;
            }

            //Point pt = Point.Empty;
            //switch (this.orientation) { 
            //    case Orientation.Vertical:
            //        pt = new Point(MousePosition.X, this.PointToScreen(new Point(0,0)).Y);
            //        break;
            //    case Orientation.Horizontal:
            //        pt = new Point(this.PointToScreen(new Point(0, 0)).X, MousePosition.Y);
            //        break;
            //}
            //Rectangle rect = new Rectangle(pt, this.splitter.Size);
            //ControlPaint.FillReversibleRectangle(rect, Color.Black);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnSplitterMoved(object sender, SplitterEventArgs e) {
            // Compute SpitterDistance with MinSize constraint.
            if (this.tempSplitterDistance <= this.panel1MinSize)
            {
                this.splitterDistance = this.panel1MinSize;
            }
            else if (this.Width - this.tempSplitterDistance < this.panel2MinSize) {
                this.splitterDistance = this.Width - this.panel2MinSize;
            }
            else
            {
                this.splitterDistance = this.tempSplitterDistance;
            }

            this.LayoutControls();

            if (this.SplitterMoved != null) {
                this.SplitterMoved(sender, e);
            }
        }
        #endregion

        #region Event Handlers
        private void splitter_MouseDown(object sender, MouseEventArgs e)
        {
            if (!this.isFixedSplitter && e.Button == MouseButtons.Left)
            {
                this.splitterMoving = true;
            }
        }

        private void splitter_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.splitterMoving)
            {
                SplitterCancelEventArgs sce = new SplitterCancelEventArgs(e.X, e.Y, this.Location.X, this.Location.Y);
                OnSplitterMoving(this, sce);
            }
        }

        private void splitter_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.splitterMoving)
            {
                OnSplitterMoved(this, new SplitterEventArgs(e.X, e.Y, this.Location.X, this.Location.Y));
                this.splitterMoving = false;
            }
        }

        private void SplitGradientContainer_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                this.splitterMoving = false;
            }
        }

        #endregion
    }
}
